home *** CD-ROM | disk | FTP | other *** search
- /*
- File: JPEGtoPICT.c
-
- Written by: Mark Krueger, Apple Computer, Inc.
-
- Contains: Converts JPEG interchange files to JPEG PICT files, which can be used by any application.
-
-
- The JPEG File Interchange Format ( JFIF) is a cross platform standard file format for
- storing JPEG compressed image files. This application shows you how you can easily convert
- these to QuickTime PICT format, or vice-verse.
-
- You can use this in your application to open JFIF files directly or put a user interface
- ( Drag and Drop would be cool ) on it to make a conversion program ). When files are to
- be used on the Mac it is best to keep them in QTPict format so they can be easily copied
- and pasted, but JFIF format is useful for transfering data to other platforms that cannot
- use PICT formated JPEG images.
-
- NOTE: the PICT to JFIF format translator is incomplete in that it only converts PICT
- files which are already in QuickTIme JPEG format and it does not handle banded JPEG
- Picts ( which may be commonly created by QuickTime applications which call
- CompressPictureFile in low memory conditions or which create their own banded picts)
- To fully handle these images, the individual bands would have to be converted into a
- single JPEG stream when put into JFIF format, and this code does not show you how
- to do that.
-
-
-
-
- */
-
-
-
- #include <Types.h>
- #include <Files.h>
- #include <Quickdraw.h>
- #include <GestaltEqu.h>
- #include <Packages.h>
- #include <Memory.h>
- #include <Fonts.h>
- #include <Events.h>
- #include <OSUtils.h>
- #include <ToolUtils.h>
- #include <Menus.h>
- #include <Dialogs.h>
- #include <stdio.h>
- //#include <Errors.h>
- #include <string.h>
- #include <StandardFile.h>
- #include "Progress.h"
-
-
- #include <ImageCompression.h>
- #include <QDOffscreen.h>
- #include "JFIFFile.h"
-
- /************************************************
- *
- * Make a new name for the compressed file ( illustrative only, this is not the recommended method ).
- *
- ************************************************/
-
- void
- GetNewName(StringPtr newName,StringPtr oldName,StringPtr ext)
- {
- long olen = oldName[0];
-
- if ( olen > (31-ext[0]) )
- olen = 31-ext[0];
- BlockMoveData(oldName+1,newName+1,olen);
- BlockMoveData(ext+1,newName+1+olen,ext[0]);
- newName[0] = olen + ext[0];
- }
-
- /************************************************
- *
- * Convert a JFIF file to a PICT .
- *
- ************************************************/
-
-
-
- OSErr ConvertFromJFIF(StringPtr name,short originalFile,PicHandle *thePicture)
- {
- OSErr result = noErr;
- short compressedFile = 0;
- OpenCPicParams header;
- Str31 newName;
- CWindowPtr window = nil;
- Rect windRect;
- long l;
- short i;
- char zero = 0;
- MatrixRecord matrix;
-
- Ptr data = nil;
- ImageDescriptionHandle desc = nil;
- PicHandle originalPicture = nil;
- SFReply sfr;
- Point pt = {100,100};
-
- ICMProgressProcRecord iProgRec = {NULL,0};
-
- /************************************************
- *
- * Extract the JPEG data if possible and create Image Description.
- *
- ************************************************/
-
- if ((desc = ScanJPEG(originalFile,&data,&header)) == nil)
- {
- result = -50;
- goto done;
- }
-
- windRect = header.srcRect;
- OffsetRect(&windRect,40,40);
-
- /************************************************
- *
- * Create a window the size of the picture, and set our port to it.
- *
- ************************************************/
-
- #ifdef SHOW_IT // make it visible if we want to see it before conversion
-
- if ((window = (CWindowPtr) NewCWindow(nil,&windRect,sfr.fName,true,0,(WindowPtr) -1L,false,0)) == nil)
- {
- result = -108;
- goto done;
- }
- #else
- if ((window = (CWindowPtr) NewCWindow(nil,&windRect,sfr.fName,false,0,(WindowPtr) -1L,false,0)) == nil)
- {
- result = -108;
- goto done;
- }
- #endif
- SetPort((GrafPtr) window);
-
- originalPicture = OpenCPicture(&header);
-
- if (originalPicture == nil)
- {
- result = memFullErr;
- goto done;
- }
-
- /************************************************
- *
- * we have to use the FDecompressImage call to make sure that the accuracy
- * param gets set to highQuality which ensures a better quality decode
- * and real dithering when going to 8-bit screens.
- *
- ************************************************/
- /*
- iProgRec.progressProc = NewICMProgressProc((ProcPtr) StdDecompressionProgressProc);
- iProgRec.progressRefCon = 'open';
- */
- SetIdentityMatrix(&matrix);
-
- if ((result = FDecompressImage( data,desc,((CGrafPtr)qd.thePort)->portPixMap,
- &header.srcRect,&matrix,ditherCopy,(RgnHandle)nil,
- (PixMapHandle)nil,(Rect *)nil,codecMaxQuality,anyCodec,0,
- (ICMDataProcRecordPtr)nil,(ICMProgressProcRecordPtr) NULL)))
- {
- ClosePicture();
- KillPicture((PicHandle) originalPicture);
- originalPicture = nil;
- goto done;
- }
- ClosePicture();
- if ( GetHandleSize((Handle)originalPicture) == sizeof(Picture) )
- {
- KillPicture((PicHandle)originalPicture);
- originalPicture = nil;
- result = memFullErr;
- goto done;
- }
-
- #ifdef SHOW_IT
-
- /************************************************
- *
- * Draw the picture in the window, slid up to the top left corner.
- *
- ************************************************/
-
- DrawPicture(originalPicture,(*originalPicture)->pictureFrame);
- #endif
-
- if ( originalPicture )
- *thePicture = originalPicture;
- else
- *thePicture = nil;
- done:
- if (iProgRec.progressProc)
- DisposeRoutineDescriptor(iProgRec.progressProc);
- if ( data )
- DisposPtr(data);
- if ( desc )
- DisposHandle((Handle)desc);
- if ( window )
- CloseWindow((WindowPtr)window);
- return result;
- }
-
- //#define USE_DATA_LOADER
- #define kDefaultBufferSize (64L * 1024L) // 64K buffer size
-
-
- OSErr ReadJFIFToGWorld(short refNum,GWorldPtr* theGWorld)
- {
- // simpler and more robust function for reading JFIF images. Because this does not go through the step of
- // decompressing the image to a picture, it requires less memory than the above function. It resizes the
- // GWorld to the size of the image. This may alter the GWorld ptr value, so be sure to copy it back to any
- // data structures that have reference to it- e.g. layers.
-
- OSErr result = noErr;
- OpenCPicParams header;
- Ptr data = NULL;
- Handle tempBuffer = NULL;
- ImageDescriptionHandle desc = NULL;
- GWorldFlags flags;
- PixMapHandle ppix;
- CGrafPtr savePort;
- GDHandle saveDevice;
- ICMProgressProcRecord iProgRec = {NULL,0};
- ICMDataProcRecord dLoadProc = {NULL,0};
-
- #ifdef USE_DATA_LOADER
- if ((desc = ScanJPEG(refNum,NULL,&header)) == NULL)
- {
- result = -50;
- goto done;
- }
- (*desc)->dataSize = 0;
- #else
- if ((desc = ScanJPEG(refNum,&data,&header)) == NULL)
- {
- result = -50;
- goto done;
- }
- #endif
- // we have a description of the image. Now resize the GWorld and set its depth. Note that we do not
- // call UpdateGWorld, but simply get rid of the existing one and reallocate it. This requires half the
- // memory it otherwise would because no copy is required. This is ok because we are not attempting to keep
- // the existing image anyway.
-
- if (*theGWorld)
- DisposeGWorld(*theGWorld);
-
- result = NewImageGWorld(theGWorld,desc,0);
-
- if (result)
- goto done;
-
- // we succeeded in changing the GWorld to the size we desire, now we simply decompress the data into
- // the GWorld's pixmap. We load the JPEG data using a small buffer to save memory, so set this up here
-
- #ifdef USE_DATA_LOADER
- dLoadProc.dataProc = NewICMDataProc((ProcPtr) JPEGDataLoader);
- dLoadProc.dataRefCon = refNum;
- result = SetFPos(refNum,fsFromStart,0);
-
- tempBuffer = NewHandleClear(kDefaultBufferSize);
- if (tempBuffer == NULL)
- {
- result = memFullErr;
- goto done;
- }
- else
- {
- MoveHHi(tempBuffer);
- HLock(tempBuffer);
-
- data = StripAddress(*tempBuffer);
- }
- #endif
-
- iProgRec.progressProc = NewICMProgressProc((ProcPtr) StdDecompressionProgressProc);
- iProgRec.progressRefCon = 'open';
-
- if (LockPixels(ppix = GetGWorldPixMap(*theGWorld)))
- {
- GetGWorld(&savePort,&saveDevice);
- SetGWorld(*theGWorld,NULL);
-
- #ifdef USE_DATA_LOADER
- result = FDecompressImage( data,desc,ppix,
- NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
- NULL,(Rect *) NULL,codecHighQuality,anyCodec,kDefaultBufferSize,
- (ICMDataProcRecordPtr) &dLoadProc,
- (ICMProgressProcRecordPtr) &iProgRec);
- #else
- result = FDecompressImage( data,desc,ppix,
- NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
- NULL,(Rect *) NULL,codecHighQuality,anyCodec,0,
- (ICMDataProcRecordPtr) NULL,
- (ICMProgressProcRecordPtr) &iProgRec);
- #endif
- SetGWorld(savePort,saveDevice);
- UnlockPixels(ppix);
- }
- else
- result = -50;
- done:
- if (tempBuffer)
- {
- HUnlock(tempBuffer);
- DisposHandle(tempBuffer);
- }
- if (desc)
- DisposHandle((Handle) desc);
- if (iProgRec.progressProc)
- DisposeRoutineDescriptor(iProgRec.progressProc);
- if (dLoadProc.dataProc)
- DisposeRoutineDescriptor(dLoadProc.dataProc);
- return result;
- }
-
-
-
- pascal OSErr JPEGDataLoader(Ptr *dataP,long bytesNeeded,long refCon)
- {
- OSErr theErr = noErr;
- short fileRefNum = LoWord(refCon);
-
- if (dataP)
- theErr = FSRead(fileRefNum,&bytesNeeded,*dataP);
- else
- theErr = SetFPos(fileRefNum,fsFromMark,bytesNeeded);
- return theErr;
- }
-
-
- /************************************************
-
- Scan a file for valid JPEG data, and fill in a picture header and ImageDescription
- for it.
-
- *************************************************/
-
- ImageDescriptionHandle ScanJPEG(short originalFile,Ptr *data,OpenCPicParams *pictureHeader)
- {
- short w,h;
- ImageDescriptionHandle desc;
- long l;
- char *bitStream,*scanData,*buffer;
- long hRes = 72L<<16,vRes = 72L<<16;
- short depth = 32;
-
- // this creates a buffer which holds the whole image. This is not very efficient. Instead we will spool
- // the data in using a data loading procedure.
-
-
- GetEOF(originalFile,&l);
- if ((buffer = NewPtr(l)) == nil)
- return(nil);
-
- FSRead(originalFile,&l,buffer);
- bitStream = buffer;
-
- if ( (desc = (ImageDescriptionHandle) NewHandle(sizeof(ImageDescription))) == nil )
- return(nil);
-
- if ( (scanData = MarkerDetect(bitStream,&w,&h,&hRes,&vRes,&depth)) == 0 )
- return(nil);
-
- (*desc)->idSize = sizeof(ImageDescription);
- (*desc)->width = w;
- (*desc)->height = h;
- (*desc)->temporalQuality = 0;
- (*desc)->spatialQuality = codecNormalQuality;
- (*desc)->dataSize = l;
- (*desc)->cType = 'jpeg';
- (*desc)->version = 0;
- (*desc)->revisionLevel = 0;
- (*desc)->vendor = 0;
- (*desc)->hRes = hRes;
- (*desc)->vRes = vRes;
- (*desc)->depth = depth;
- (*desc)->clutID = -1;
- BlockMoveData("\pPhoto",(*desc)->name,6);
- SetRect(&pictureHeader->srcRect,0,0,w,h);
- pictureHeader->version = -2;
- pictureHeader->reserved1 = 0;
- pictureHeader->reserved2 = 0;
- pictureHeader->hRes = hRes;
- pictureHeader->vRes = vRes;
-
- if (data == NULL)
- {
- // get rid of this buffer- we are going to load the image in small pieces
-
- DisposePtr(buffer);
- }
- else
- *data = buffer;
-
- return(desc);
- }
-
-
-
- /**********************************************************************
-
- JPEG specific stuff.
-
- ***********************************************************************/
-
- /*
-
- JPEG Marker code definitions.
-
- */
-
- #define MARKER_PREFIX 0xff
- #define MARKER_SOI 0xd8 /* start of image */
- #define MARKER_SOF 0xc0 /* start of frame */
- #define MARKER_DHT 0xc4 /* define Huffman table */
- #define MARKER_EOI 0xd9 /* end of image */
- #define MARKER_SOS 0xda /* start of scan */
- #define MARKER_DQT 0xdb /* define quantization tables */
- #define MARKER_DNL 0xdc /* define quantization tables */
- #define MARKER_DRI 0xdd /* define Huffman table */
- #define MARKER_COM 0xfe /* comment */
- #define MARKER_APP0 0xe0
-
-
- /**********************************************************************
-
- Read the quantization table from the JPEG bitstream.
-
- ***********************************************************************/
-
- void
- SwallowQuantTable(char *data)
- {
- long i;
- long length,pm,nm;
-
- length = *(short *)data; /* read length */
- length -= 2;
- data += 2;
- while ( length ) {
- nm= *data++; /* read precision and number */
- pm = nm>>4;
- nm &= 0xf;
- length--;
- if ( pm ) {
- for(i=0;i<64;i++) {
- length -= 2;
- data += 2;
- }
- } else {
- for(i=0;i<64;i++) {
- length--;
- data++;
- }
- }
- }
- }
-
- /**********************************************************************
-
- Read the huffman table from the JPEG bitstream.
-
- ***********************************************************************/
-
- void
- SwallowHuffTable(char *data)
- {
- short i,tc,id;
- long length;
-
- unsigned char bin[17];
- unsigned char val[256];
-
- bin[0] = 0;
- length = *(short *)data; /* read length */
- data += 2;
- length -= 2;
- while ( length ) {
- id=*data++; /* read id */
- length--;
- if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
- return;
- }
- tc = 0;
- for(i=0;i<16;i++) {
- length--;
- tc += (bin[i+1] = *data++);
- }
- for (i=0; i < tc; i++ ) {
- length--;
- val[i] = *data++;
- }
- }
- }
-
-
-
- /**********************************************************************
-
- Scan the JPEG stream for the proper markers and fill in the image parameters
-
- returns nil if it cant comprehend the data, otherwise a pointer to the start
- of the JPEG data.
-
-
- It does a cursory check on the JPEG data to see if it's reasonable.
- Check out the ISO JPEG spec if you really want to know what's going on here.
-
- ***********************************************************************/
-
- char *
- MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth)
- {
- short frame_field_length;
- short data_precision;
- short scan_field_length;
- short number_component,scan_components;
- short c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
- short dac_t1, dac_t2, dac_t3;
- unsigned char c;
- short qtabledefn;
- short htabledefn;
- short status;
- short length;
- short i;
-
- c = *data++;
- qtabledefn = 0;
- htabledefn = 0;
- status = 0;
- while (c != (unsigned char)MARKER_SOS) {
- while (c != (unsigned char)MARKER_PREFIX)
- c = *data++; /* looking for marker prefix bytes */
- while (c == (unsigned char)MARKER_PREFIX)
- c = *data++; /* (multiple?) marker prefix bytes */
- if (c == 0)
- continue; /* 0 is never a marker code */
-
- if (c == (unsigned char)MARKER_SOF) {
-
- frame_field_length = *(short *)data;
- data += 2;
- data_precision = *data++;
-
- if ( data_precision != 8 ) {
- status = 2;
- }
-
- *height = *(short *)data;
- data += 2;
- *width = *(short *)data;
- data += 2;
-
- number_component = *data++;
-
- switch ( number_component ) {
- case 3:
- c1 = *data++;
- hv1 = *data++;
- q1 = *data++;
- c2 = *data++;
- hv2 = *data++;
- q2 = *data++;
- c3 = *data++;
- hv3 = *data++;
- q3 = *data++;
- *depth = 32;
- break;
- case 1:
- c1 = *data++;
- hv1 = *data++;
- q1 = *data++;
- *depth = 40;
- break;
- default:
- status = 3;
- break;
- }
- continue;
- }
-
- if (c == (unsigned char)MARKER_SOS) {
- short tn;
- scan_field_length = *(short *)data;
- data += 2;
- scan_components = *data++;
- for ( i=0; i < scan_components; i++ ) {
- unsigned char cn,dac_t;
-
- cn = *data++;
- dac_t = *data++;
- if ( cn == c1 ) {
- dac_t1 = dac_t;
- } else if ( cn == c2 ) {
- dac_t2 = dac_t;
- } else if ( cn == c3 ) {
- dac_t3 = dac_t;
- } else {
- status = 29;
- break;
- }
- }
- switch ( tn=(dac_t1 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
- switch ( tn=(dac_t2 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
- switch ( tn=(dac_t3 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
-
-
- /* Initialize the DC tables */
-
- switch ( tn=dac_t1 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- switch ( tn=dac_t2 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- switch ( tn=dac_t3 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- if ( *data++ != 0 ) {
- // status = 18;
- }
- if ( *data++ != 63 ) {
- // status = 19;
- }
- if ( *data++ != 0 ) {
- // status = 20;
- }
- if ( status )
- return(0);
- else
- return(data);
- }
-
- if (c == (unsigned char)MARKER_DQT) {
- scan_field_length = *(short *)data;
- SwallowQuantTable(data);
- data += scan_field_length;
- continue;
- }
- if (c == (unsigned char)MARKER_DHT) {
- scan_field_length = *(short *)data;
- SwallowHuffTable(data);
- continue;
- }
- if (c == (unsigned char)MARKER_DRI) {
- length = *(short *)data; /* read length */
- data += 2;
- length = *(short *)data;
- data += 2;
- continue;
- }
- if (c == (unsigned char)MARKER_DNL) {
- length = *(short *)data; /* read length */
- data += 2;
- length = *(short *)data;
- data += 2;
- continue;
- }
- if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
- continue;
- }
-
- if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI) /* image start, end marker */
- continue;
-
- if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
- status = 12;
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
- length = *(short *)data; /* read length */
- data += 2;
- length -= 2;
- if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
- char buf[5];
- buf[0] = *data++;
- buf[1] = *data++;
- buf[2] = *data++;
- buf[3] = *data++;
- buf[4] = *data++;
- length -= 5;
-
- if ( buf[0] == 'J' && buf[1] == 'F' && buf[2] == 'I' && buf[3] == 'F' ) {
- short units;
- long xres,yres;
- short version;
-
-
- version = *(short *)data; data += 2;length -= 2;
- if ( version != 0x100 && version != 0x101 && version != 0x102) {
- status = 44; // unknown JFIF version
- break;
- }
- units = *data++; length--;
- xres = *(short *)data; data += 2; length -= 2;
- yres = *(short *)data; data += 2; length -= 2;
-
- switch ( units ) {
- case 0: // no res, just aspect ratio
- // some files will have bad res data here. In this case, xRes & yRes will
- // be zero. This really means that they should be 1! This allows some dodgy
- // JPEGs created by some crappy PC software to be opened
-
- if (xres == 0)
- xres = 1;
- if (yres == 0)
- yres = 1;
-
- *hRes = FixMul(72L<<16,xres<<16);
- *vRes = FixMul(72L<<16,yres<<16);
- break;
- case 1: // dots per inch
- *hRes = xres<<16;
- *vRes = yres<<16;
- break;
- case 2: // dots per centimeter (we convert to dpi )
- *hRes = FixMul(0x28a3d,xres<<16);
- *vRes = FixMul(0x28a3d,xres<<16);
- break;
- default:
- break;
- }
- xres = *data++; length--;
- yres = *data++; length--;
-
- /* skip JFIF thumbnail */
-
- xres *= yres;
- data += xres*3; length -= xres*3;
-
- if ( length != 0 ) {
- status = 44; // bad jfif marker
- break;
- }
- }
- }
- data += length;
- continue;
- }
- if (c == (unsigned char)MARKER_COM) {
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if ( c == 0x1 )
- continue;
- if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
- length = *(short *)data; /* read length */
- status = 13;
- data += length;
- continue;
- }
- }
- return(0);
- }
-
-
-
-
-
-
-
-